/**
 * Copyright (c) 2009, Robert Schuster
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the
 * following conditions are met:
 *
 * - Redistributions of source code must retain the above
 *   copyright notice, this list of conditions and the
 *   following disclaimer.
 * - Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the
 *   following disclaimer in the documentation and/or other
 *   materials provided with the distribution.
 * - Neither the name of Robert Schuster nor the names of his
 *   contributors may be used to endorse or promote products
 *   derived from this software without specific prior written
 *   permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package jclcalc;

/**
 *
 * @author robert schuster
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        //init the Library
        JCLcalc.CLcalc_init();

        //the name of the device
        System.out.println("Device:          " + JCLcalc.CLcalc_getDeviceName(JCLcalc.CLcalc_DEVICE_gpu0));
        int[] speed = JCLcalc.CLcalc_getDeviceSpeed(JCLcalc.CLcalc_DEVICE_gpu0);
        System.out.println("Speed per Core:  " + speed[0] + " MHz");
        System.out.println("Number of Cores: " + speed[1]);
        System.out.println("Speed*Cores:     " + speed[2] + " MHz\n");

        String kernel = "\n"+
                "template <typename T> \n" +
                "#pragma OPENCL EXTENSION cl_khr_fp64 : enable \n" +
                "__kernel void sqrt1d (__global T* input, __global T* output) {\n" +
                "    int index = get_global_id(0); \n" +
                "    output[index] = round(sqrt(input[index]))*input[index];\n" +
                "}";

        //create some sample data
        int size = 10000000;
        double[] arg1 = new double[size];
        double[] arg2 = new double[size];

        for (int i=0;i<size;i++)
            arg1[i] = i/1001.7f;

        //create a new Executor-Object
        Executor exe = new Executor(JCLcalc.CLcalc_DEVICE_cpu0);

        //have a look at the time the calculation takes on the GPU
        //if you start the time measurement here, then the compile-time is
        //included. You don't need to compile a progam more then one time.
        long start = System.currentTimeMillis();

        try {
            //create a new Program-Object inside the Executor
            int ProgID = exe.addProgramFromSource(kernel, "sqrt1d<double>");

            //add data to the device
            long output_typ = JCLcalc.CL_MEM_USE_HOST_PTR;
            exe.addData(0, arg1, JCLcalc.CL_MEM_USE_HOST_PTR);
            exe.addData(0, arg2, output_typ);

            //set the arguments of the kernel
            exe.setArgument(ProgID, 0, arg1);
            exe.setArgument(ProgID, 1, arg2);

            //execute the kernel once for every element of the input array
            exe.Execute(0, ProgID, size);

            //read in the data, you only need that if you don't use host-pointer
            if (output_typ != JCLcalc.CL_MEM_USE_HOST_PTR)
                exe.getData(0, arg2);

            //release the data
            exe.releaseData(arg1);
            exe.releaseData(arg2);
            
        } catch (Error ex) {
            System.err.println("Error: " + ex.id);
            System.err.println("Error Text: \n" + ex.Text);
            ex.printStackTrace();
            System.exit(0);
        }

        //how much time?
        long ende = System.currentTimeMillis();
        System.out.print("JCLcalc result: Time: " + (ende-start) + "ms");
        System.out.println(" arg2[size-1] = " + arg2[size-1]);

        //compare the speed with the same calculation done inside of java
        start = System.currentTimeMillis();
        for (int i=0;i<size;i++)
            arg2[i] = (float) Math.round((float)Math.sqrt(arg1[i])) * arg1[i];
        ende = System.currentTimeMillis();

        System.out.print("Java result:    Time: " + (ende-start) + "ms");
        System.out.println(" arg2[size-1] = " + arg2[size-1]);

        //shutdown the library
        JCLcalc.CLcalc_shutdown();
    }

}
